"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

require("core-js/modules/es.array.slice");

require("core-js/modules/es.function.name");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));

var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));

var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));

var _logger = _interopRequireDefault(require("./utils/logger.js"));

var _loader = require("./loader.js");

var _exception = require("./utils/exception.js");

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

/* Notice: ms-stream may cause IE/Edge browser crash if seek too frequently!!!
 * The browser may crash in wininet.dll. Disable for now.
 *
 * For IE11/Edge browser by microsoft which supports `xhr.responseType = 'ms-stream'`
 * Notice that ms-stream API sucks. The buffer is always expanding along with downloading.
 *
 * We need to abort the xhr if buffer size exceeded limit size (e.g. 16 MiB), then do reconnect.
 * in order to release previous ArrayBuffer to avoid memory leak
 *
 * Otherwise, the ArrayBuffer will increase to a terrible size that equals final file size.
 */
var MSStreamLoader = /*#__PURE__*/function (_BaseLoader) {
  (0, _inherits2.default)(MSStreamLoader, _BaseLoader);

  var _super = _createSuper(MSStreamLoader);

  (0, _createClass2.default)(MSStreamLoader, null, [{
    key: "isSupported",
    value: function isSupported() {
      try {
        if (typeof self.MSStream === 'undefined' || typeof self.MSStreamReader === 'undefined') {
          return false;
        }

        var xhr = new XMLHttpRequest();
        xhr.open('GET', 'https://example.com', true);
        xhr.responseType = 'ms-stream';
        return xhr.responseType === 'ms-stream';
      } catch (e) {
        _logger.default.w('MSStreamLoader', e.message);

        return false;
      }
    }
  }]);

  function MSStreamLoader(seekHandler, config) {
    var _this;

    (0, _classCallCheck2.default)(this, MSStreamLoader);
    _this = _super.call(this, 'xhr-msstream-loader');
    _this.TAG = 'MSStreamLoader';
    _this._seekHandler = seekHandler;
    _this._config = config;
    _this._needStash = true;
    _this._xhr = null;
    _this._reader = null; // MSStreamReader

    _this._totalRange = null;
    _this._currentRange = null;
    _this._currentRequestURL = null;
    _this._currentRedirectedURL = null;
    _this._contentLength = null;
    _this._receivedLength = 0;
    _this._bufferLimit = 16 * 1024 * 1024; // 16MB

    _this._lastTimeBufferSize = 0;
    _this._isReconnecting = false;
    return _this;
  }

  (0, _createClass2.default)(MSStreamLoader, [{
    key: "destroy",
    value: function destroy() {
      if (this.isWorking()) {
        this.abort();
      }

      if (this._reader) {
        this._reader.onprogress = null;
        this._reader.onload = null;
        this._reader.onerror = null;
        this._reader = null;
      }

      if (this._xhr) {
        this._xhr.onreadystatechange = null;
        this._xhr = null;
      }

      (0, _get2.default)((0, _getPrototypeOf2.default)(MSStreamLoader.prototype), "destroy", this).call(this);
    }
  }, {
    key: "open",
    value: function open(dataSource, range) {
      this._internalOpen(dataSource, range, false);
    }
  }, {
    key: "_internalOpen",
    value: function _internalOpen(dataSource, range, isSubrange) {
      this._dataSource = dataSource;

      if (!isSubrange) {
        this._totalRange = range;
      } else {
        this._currentRange = range;
      }

      var sourceURL = dataSource.url;

      if (this._config.reuseRedirectedURL) {
        if (this._currentRedirectedURL != undefined) {
          sourceURL = this._currentRedirectedURL;
        } else if (dataSource.redirectedURL != undefined) {
          sourceURL = dataSource.redirectedURL;
        }
      }

      var seekConfig = this._seekHandler.getConfig(sourceURL, range);

      this._currentRequestURL = seekConfig.url;
      var reader = this._reader = new self.MSStreamReader();
      reader.onprogress = this._msrOnProgress.bind(this);
      reader.onload = this._msrOnLoad.bind(this);
      reader.onerror = this._msrOnError.bind(this);
      var xhr = this._xhr = new XMLHttpRequest();
      xhr.open('GET', seekConfig.url, true);
      xhr.responseType = 'ms-stream';
      xhr.onreadystatechange = this._xhrOnReadyStateChange.bind(this);
      xhr.onerror = this._xhrOnError.bind(this);

      if (dataSource.withCredentials) {
        xhr.withCredentials = true;
      }

      if ((0, _typeof2.default)(seekConfig.headers) === 'object') {
        var headers = seekConfig.headers;

        for (var key in headers) {
          if (headers.hasOwnProperty(key)) {
            xhr.setRequestHeader(key, headers[key]);
          }
        }
      } // add additional headers


      if ((0, _typeof2.default)(this._config.headers) === 'object') {
        var _headers = this._config.headers;

        for (var _key in _headers) {
          if (_headers.hasOwnProperty(_key)) {
            xhr.setRequestHeader(_key, _headers[_key]);
          }
        }
      }

      if (this._isReconnecting) {
        this._isReconnecting = false;
      } else {
        this._status = _loader.LoaderStatus.kConnecting;
      }

      xhr.send();
    }
  }, {
    key: "abort",
    value: function abort() {
      this._internalAbort();

      this._status = _loader.LoaderStatus.kComplete;
    }
  }, {
    key: "_internalAbort",
    value: function _internalAbort() {
      if (this._reader) {
        if (this._reader.readyState === 1) {
          // LOADING
          this._reader.abort();
        }

        this._reader.onprogress = null;
        this._reader.onload = null;
        this._reader.onerror = null;
        this._reader = null;
      }

      if (this._xhr) {
        this._xhr.abort();

        this._xhr.onreadystatechange = null;
        this._xhr = null;
      }
    }
  }, {
    key: "_xhrOnReadyStateChange",
    value: function _xhrOnReadyStateChange(e) {
      var xhr = e.target;

      if (xhr.readyState === 2) {
        // HEADERS_RECEIVED
        if (xhr.status >= 200 && xhr.status <= 299) {
          this._status = _loader.LoaderStatus.kBuffering;

          if (xhr.responseURL != undefined) {
            var redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL);

            if (xhr.responseURL !== this._currentRequestURL && redirectedURL !== this._currentRedirectedURL) {
              this._currentRedirectedURL = redirectedURL;

              if (this._onURLRedirect) {
                this._onURLRedirect(redirectedURL);
              }
            }
          }

          var lengthHeader = xhr.getResponseHeader('Content-Length');

          if (lengthHeader != null && this._contentLength == null) {
            var length = parseInt(lengthHeader);

            if (length > 0) {
              this._contentLength = length;

              if (this._onContentLengthKnown) {
                this._onContentLengthKnown(this._contentLength);
              }
            }
          }
        } else {
          this._status = _loader.LoaderStatus.kError;

          if (this._onError) {
            this._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, {
              code: xhr.status,
              msg: xhr.statusText
            });
          } else {
            throw new _exception.RuntimeException('MSStreamLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText);
          }
        }
      } else if (xhr.readyState === 3) {
        // LOADING
        if (xhr.status >= 200 && xhr.status <= 299) {
          this._status = _loader.LoaderStatus.kBuffering;
          var msstream = xhr.response;

          this._reader.readAsArrayBuffer(msstream);
        }
      }
    }
  }, {
    key: "_xhrOnError",
    value: function _xhrOnError(e) {
      this._status = _loader.LoaderStatus.kError;
      var type = _loader.LoaderErrors.EXCEPTION;
      var info = {
        code: -1,
        msg: e.constructor.name + ' ' + e.type
      };

      if (this._onError) {
        this._onError(type, info);
      } else {
        throw new _exception.RuntimeException(info.msg);
      }
    }
  }, {
    key: "_msrOnProgress",
    value: function _msrOnProgress(e) {
      var reader = e.target;
      var bigbuffer = reader.result;

      if (bigbuffer == null) {
        // result may be null, workaround for buggy M$
        this._doReconnectIfNeeded();

        return;
      }

      var slice = bigbuffer.slice(this._lastTimeBufferSize);
      this._lastTimeBufferSize = bigbuffer.byteLength;
      var byteStart = this._totalRange.from + this._receivedLength;
      this._receivedLength += slice.byteLength;

      if (this._onDataArrival) {
        this._onDataArrival(slice, byteStart, this._receivedLength);
      }

      if (bigbuffer.byteLength >= this._bufferLimit) {
        _logger.default.v(this.TAG, "MSStream buffer exceeded max size near ".concat(byteStart + slice.byteLength, ", reconnecting..."));

        this._doReconnectIfNeeded();
      }
    }
  }, {
    key: "_doReconnectIfNeeded",
    value: function _doReconnectIfNeeded() {
      if (this._contentLength == null || this._receivedLength < this._contentLength) {
        this._isReconnecting = true;
        this._lastTimeBufferSize = 0;

        this._internalAbort();

        var range = {
          from: this._totalRange.from + this._receivedLength,
          to: -1
        };

        this._internalOpen(this._dataSource, range, true);
      }
    }
  }, {
    key: "_msrOnLoad",
    value: function _msrOnLoad(e) {
      // actually it is onComplete event
      this._status = _loader.LoaderStatus.kComplete;

      if (this._onComplete) {
        this._onComplete(this._totalRange.from, this._totalRange.from + this._receivedLength - 1);
      }
    }
  }, {
    key: "_msrOnError",
    value: function _msrOnError(e) {
      this._status = _loader.LoaderStatus.kError;
      var type = 0;
      var info = null;

      if (this._contentLength && this._receivedLength < this._contentLength) {
        type = _loader.LoaderErrors.EARLY_EOF;
        info = {
          code: -1,
          msg: 'MSStream meet Early-Eof'
        };
      } else {
        type = _loader.LoaderErrors.EARLY_EOF;
        info = {
          code: -1,
          msg: e.constructor.name + ' ' + e.type
        };
      }

      if (this._onError) {
        this._onError(type, info);
      } else {
        throw new _exception.RuntimeException(info.msg);
      }
    }
  }]);
  return MSStreamLoader;
}(_loader.BaseLoader);

var _default = MSStreamLoader;
exports.default = _default;